-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Stabilize if let
guards (feature(if_let_guard)
)
#141295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
r? @SparrowLii rustbot has assigned @SparrowLii. Use |
Some changes occurred to the CTFE machinery Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt Some changes occurred in compiler/rustc_codegen_ssa |
6fe74d9
to
5ee8970
Compare
rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead. cc @rust-lang/rust-analyzer Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
eb0e4b4
to
0358002
Compare
This comment has been minimized.
This comment has been minimized.
92a5204
to
ab138ce
Compare
This comment has been minimized.
This comment has been minimized.
5ceca48
to
a20c4f6
Compare
This comment has been minimized.
This comment has been minimized.
1dd9974
to
5796073
Compare
cc @Nadrieril |
This needs a fcp so I'd like to roll this to someone more familiar with this feature |
r? @est31 |
831fca2
to
6270aba
Compare
266e9d9
to
58b02c9
Compare
Thanks for your comment! Just to clarify, the drop order appears to be consistent across all editions. Could you please clarify what you mean by the potential confusion or breaking change for users? Are you referring to specific behavioral differences or edge cases that might cause code to behave differently when moving from a normal It would be helpful to understand the exact scenarios you’re concerned about |
This comment has been minimized.
This comment has been minimized.
fdec32f
to
ba28c26
Compare
This comment has been minimized.
This comment has been minimized.
0699298
to
d6688ef
Compare
We reviewed this briefly in the lang triage call today. We'll all probably need to have a closer look at this, and we'll of course be particularly interested in confirming that the drop order here is what we expect. |
I'm missing context, what breakage are you talking about? @Kivooeo has confirmed that moving from a |
@traviscross I need your advice on this, would writing MIR drop order test for if-let guard will help team review it better and more precisely along side two existed (non MIR tests) that created for drop order: compare-drop-order, drop-order The same way it was made for let chains back in the day here mir-match-guard-let-chains-drop-order |
@Kivooeo the test you link is not a mir test, it's a normal (ui) test in the "mir" directory. A real MIR test is not necessary for this feature, since drop order can be observed with normal tests such as the ones you point to. It also seems to me that the existing tests are sufficient to demonstrate the drop order, unless there's a corner case I'm not thinking of. |
@Nadrieril,
Well, I thought that this is a MIR test because it was in mir folder, hm, interesting, is there a real MIR tests? I guess it should do something like comparing right MIR output to that one that created by programm or something
|
@traviscross as a good starting point, you can look at the tests added by #140981. The PR description gives an overview. |
After we accepted rust-lang/rfcs#2294, we accepted: Work is ongoing on the implementation of that. If you would, please include analysis and discussion in the stabilization report about the expected interaction between these features, and in particular, anything about this stabilization that might commit us to decisions about how guard patterns would have to work in combination with let guards or that might make the later stabilization of guard patterns more challenging. |
This comment has been minimized.
This comment has been minimized.
203c6fe
to
d2cc457
Compare
@traviscross about the guard patterns feature and future interactions between if let guard, is there way i can check this locally at the moment? because if i get rfc right it allows us to use this syntax match x {
Some(x if x > 5) => (),
None => (),
} but when im trying to use #![feature(guard_patterns)] it's not let me do this and saying that this feature is incomplete, so it's just should be theoretical analysis of their possible future interactions, right? i also should clarify if we looking into possibilities of this syntax like this
or only for something like this
because there was a message about this |
9e373c5
to
02361f2
Compare
@Kivooeo; you'll probably just want to ask and talk with the people working on it, particularly @max-niederman, @dianne, and @Nadrieril. |
Here's my initial read on the interactions:
|
Yes, that's correct. The feature is currently not fully implemented so it would just be analyzing future interactions.
As far as I'm aware, there hasn't been significant discussion of this yet, and we could go either way. The most conservative option would be to keep the special case for match arm guards and only allow |
Guard patterns are not implemented yet, it's normal that you can't experiment with it at the moment. I do not foresee tricky interactions, curious if @dianne you can think of anything. My reasoning is as follows: pattern guards basically desugar to expanding or-patterns and moving the inside guards to be normal guards. So whatever the behavior of normal guards is what we'll get for guard patterns and that's probably ok. Plus, honestly, I actually don't see any way in which if let guards could be implemented differently. If someone has an example in mind I'm all ears. EDIT: looks like we all replied simultaneously |
updated PR description, Also want to add my two cents on this, if i get everything right about how guard patterns should work It desugar this Some(x if x > 5) to this Some(x) if x > 5 so, technically, after if let guard get stabilized Some(x if let Some(y) = y && x > 5) will desugar to this Some(x) if let Some(y) = y && x > 5 which is completly correct code with if let guard feature even if user for some reason will write something like this Some(if x > 5 && let Some(y) = y) it also will desugars to correct code like Some(x) if x > 5 && let Some(y) = y so i currently see no problems with this features work together correct me if im wrong |
Yes, that's roughly how it works. Sorry if the following is off-topic/pedantic (hopefully it helps illustrate why these features work okay together!): technically there's not a desugaring pass in the implementation1, but the semantics are equivalent, so it's easiest to think of it as desugaring. Guard patterns are never represented as arm guards, but they do share a representation after a certain point in compilation. The representation you get from guard patterns in matches is the same as if you'd expanded out or-patterns and then written the guards as arm guards. e.g., Some(pat if guard1) if guard2 => /* ... */, has the same runtime behavior as Some(pat) if guard1 && guard2 => /* ... */, and [1 if guard1, (2 if guard2) | (3 if guard3), 4 if guard4] => /* ... */, has the same runtime behavior as [1, 2, 4] if guard1 && guard2 && guard4 => /* ... */,
[1, 3, 4] if guard1 && guard3 && guard4 => /* ... */, You can also use guard patterns outside of matches, despite there not being arms to desugar to, e.g. let (((min, max) if min <= max) | (max, min)) = (x, y); which is where some of the subtleties crop up. Footnotes
|
Summary
This proposes the stabilization of
if let
guards (tracking issue: #51114, RFC: rust-lang/rfcs#2294). This feature enhances Rust's pattern matching by allowingif let
expressions to be used directly within match arm guards, enabling more expressive, concise, and readable conditional logic in match statements.What is being stabilized
The ability to use
if let
expressions within match arm guards is being stabilized. This allows for conditional pattern matching directly within the guard clause of a match arm, combining pattern destructuring with boolean conditions.Example:
Motivation
The primary motivation for if let guards is to reduce boilerplate and improve the clarity of conditional logic within match statements. Prior to this feature, complex conditional checks often required nested if let statements within the match arm's body, leading to increased indentation and reduced readability.
Consider the following scenario without if let guards:
With if let guards, this becomes significantly more streamlined:
Also I should make a remark here and say that drop order or other things are identical in this both contructions, so think about
if let
guards as just like about easirer way to express thisImplementation and Testing
The if let guard feature has undergone extensive implementation and testing to ensure its stability, correctness, and consistent behavior across all Rust editions. This process has involved collaborative efforts, notably with @est31, who has provided invaluable insights and thorough testing, confirming identical functionality across editions.
Tests
Error messages and diagnostics
warns.rs, parens.rs, macro-expanded.rs, guard-mutability-2.rs, ast-validate-guards.rs - shows that
if let
guards are parsed with strict syntax rules, disallowing parentheses around thelet
expression and rejecting macro expansions that produce statements instead of expressions. The compiler correctly diagnoses irrefutable patterns, unreachable patterns, mutability violations, and unsupported constructs inside guards, ensuring soundness and clear error messages. The parser fails on invalid syntax without complex recovery, preventing cascading errors and maintaining clarityScoping and shadowing
scope.rs - verifies that bindings created inside if let match guards are properly scoped and usable in the corresponding match arm. Covers both let on the left-hand side and right-hand side of &&
shadowing.rs - validates that name shadowing works correctly within if let guards and does not lead to resolution issues. Demonstrates deep shadowing via multiple lets and ensures type resolution matches expected shadowed bindings
scoping-consistency.rs - ensures that temporaries created within
if let
guards are correctly scoped to the guard expression and remain valid for the duration of the match arm they’re used inExhaustiveness
exhaustive.rs - validates that
if let
guards do not affect exhaustiveness checking in match expressions, test fails due missing match armType System
type-inference.rs - confirms that type inference works correctly in match guards using
if let
typeck.rs - verifies that type mismatches in
if let
guards are caught as expected — the same way they are in other contextsDrop
drop-order.rs - ensures that temporaries created in match guards are dropped in the correct order
compare-drop-order.rs - comparing drop order between regular
if let
in match arm andif let
guard between all editions to show that drop order across all editions the samedrop-score.rs - ensures that temporaries introduced in
if let
guards (includinglet chains
) live for the duration of the armdrop-order-comparisons.rs - compares evaluation and drop order between various kinds of let chains
Move
move-guard-if-let.rs, move-guard-if-let-chain.rs - tests verify that the borrow checker correctly understands how moves happen inside
if let
guards, especially withlet
chains. Specifically, a move of a variable inside a guard pattern only actually occurs if that guard pattern matches. This means the move is conditional, and the borrow checker must track this precisely to avoid false move errors or unsound behaviorKey aspects verified during testing include:
&&
: Multiplelet
bindings can be chained within a single guard using the&&
operator.if let
guard can be refutable, allowing for concise conditional logic based on the success or failure of the pattern match.if let
guards has been carefully reviewed to ensure it aligns with the expected runtime behavior, particularly concerning variable lifetimes and drop order.Concers about all editions and drop order
Important
Unlike
let chains
inwhile
and regularif
the drop order inif let
guard is predictable and stable across all editions and even if we writeslet chains
inif let
guard it also works stable across all editions, this was tested by #140981 and compare-drop-order.rsAny initial concerns regarding temporary drop order or variable scoping in older editions were resolved by backported compiler improvements, ensuring consistent and correct behavior across all editions
Analysis on interactions with guard patterns
Some(x if let Some(y) = y && x > 5)
— but again, this can be addressed when guard patterns are fully designed and doesn’t seem like a blocker nowSo based on this, it looks like we’re in a good position to move forward with if let guards without causing problems for future features.
Unresolved Issues
let chains
insideif let
guard is the sameNote
This is my first stabilization PR, so if I missed any steps or there’s something I should adjust, please feel free to point it out — I’d be happy to improve it. I’ve followed the standard process as closely as I could, but I’m still learning the full stabilization workflow
Related
if let
guards documentation reference#1823 reviewed by @WaffleLapkin (we put it off untill this feature get stabilized)if let
overall, that was a good discussion where we tested things